package cn.liumouren.tool.security.core;

import org.springframework.core.Ordered;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * {@code @Order}、{@code Ordered}不影响类的加载顺序, 只影响执行顺序
 *
 * @author daniel Liu
 * @date 2021/3/10 16:19
 */
public abstract class DlFilter extends OncePerRequestFilter implements Ordered {
    /**
     * 过滤器作用的 urlPatterns
     */
    private final Set<String> urlPatterns = new HashSet<>();
    /**
     * 过滤器排除的 urlPatterns
     */
    private final Set<String> excludePatterns = new HashSet<>();
    /**
     * 做路由匹配
     */
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    /**
     * order越小优先级越高
     */
    private int order = 0;

    @Override
    public void afterPropertiesSet() {
    }

    public final DlFilter addPatterns(String... urlPatterns) {
        Assert.notNull(urlPatterns, "urlPatterns not null.");
        this.urlPatterns.addAll(Arrays.asList(urlPatterns));
        return this;
    }

    public final DlFilter excludePatterns(String... urlPatterns) {
        Assert.notNull(urlPatterns, "urlPatterns not null.");
        this.excludePatterns.addAll(Arrays.asList(urlPatterns));
        return this;
    }

    /**
     * 子类需重写
     *
     * @param request     HttpServletRequest
     * @param response    HttpServletResponse
     * @param filterChain FilterChain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected abstract void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException;

    @Override
    protected final boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        return notWorkInPath(request.getServletPath());
    }

    /**
     * 判断当前path是否不经过filter
     *
     * @param path path
     * @return 是否不经过当前path
     */
    private boolean notWorkInPath(String path) {
        path = processPath(path);
        // 先判断是否在excludePath里, 在直接返回true
        for (String exclude : excludePatterns) {
            if (antPathMatcher.match(exclude, path)) {
                return true;
            }
        }
        // 再判断是否在需要过滤的urls里
        for (String url : urlPatterns) {
            if (antPathMatcher.match(url, path)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 如果是 "" 返回 /
     * <p>如果是 /user/ 返回 /user
     *
     * @param path
     * @return
     */
    private String processPath(String path) {
        // 防止 /user/ 和 /* 不匹配
        if (path.length() > 1 && path.endsWith("/")) {
            return path.substring(0, path.length() - 1);
        }
        // 防止 "" 和 /* 不匹配
        if ("".equals(path)) {
            return "/";
        }
        return path;
    }

    @Override
    public final int getOrder() {
        return order;
    }

    /**
     * 默认为0, 值越小优先级越高
     *
     * @param order order
     */
    public final void setOrder(int order) {
        this.order = order;
    }
}
